<?php

namespace Model;

class User extends Model {
    const HASHMETHOD_NONE = 'none';
    const HASHMETHOD_UNKNOWN = 'unknown';
    const HASHMETHOD_MD5 = 'md5';
    const HASHMETHOD_SHA1 = 'sha1';
    const HASHMETHOD_PWDHASH = 'pwdhash';
    
    protected $_data;
    
    public function usersLocatedPath() {
        return $this->core->storagePath('users');
    }
    
    public function getAll($cache=true) {
        if(!$cache) {
            $this->_data = null;
        }
        
        if($this->_data) {
           return $this->_data; 
        }
        
        $storePath = $this->usersLocatedPath();

        if(!file_exists($storePath)) {
            return [];
        }

        $content = file_get_contents($storePath);

        $allUserAry = array_map(function ($row) {
            return explode('=', $row, 2);
        }, array_filter(array_map('trim', explode("\n", $content))));

        $allUsers = [];

        foreach ($allUserAry as $user) {
            $username = $user[0];
            $userData = $user[1];
            
            list($hash, $hashdata) = explode(',', $userData, 2);
            
            $allUsers [$username] = [
                'hash_method' => $hash,
                'hash_data' => $hashdata
            ];
        }

        return $this->_data = $allUsers;
    }
    
    public function exists($username) {
        return array_key_exists($username, $this->getAll());
    }
    
    public function getAvailableHashMethods() {
        return [
            self::HASHMETHOD_MD5,
            self::HASHMETHOD_PWDHASH,
            self::HASHMETHOD_SHA1,
        ];
    }
    
    public function add($username) {
        if($this->exists($username)) {
            return false;
        }
        
        $this->_data[$username] = ['hash_method' => self::HASHMETHOD_UNKNOWN];
        return $this->save();
    }
    
    public function del($username) {
        if(!$this->exists($username)) {
            return false;
        }
        
        unset($this->_data[$username]);
        return $this->save();
    }
    
    public function updatePassword($username, $password, $hashMethod=self::HASHMETHOD_PWDHASH) {
        $key = strtolower(base_convert(mt_rand(0, 255), 10, 16));
        $users = $this->getAll();
        $users[$username]['hash_method'] = $hashMethod;
        
        switch ($hashMethod) {
            case self::HASHMETHOD_MD5: 
                $users[$username]['hash_data'] = strtolower(md5($key . $password) . $key);
                break;
            case self::HASHMETHOD_SHA1:
                $users[$username]['hash_data'] = strtolower(sha1($key . $password) . $key);
                break;
            case self::HASHMETHOD_PWDHASH:
                $users[$username]['hash_data'] = password_hash($password, PASSWORD_DEFAULT);
                break;
            case self::HASHMETHOD_NONE:
            case self::HASHMETHOD_UNKNOWN:
                $users[$username]['hash_data'] = '';
                break;
        }
        
        $this->_data = $users;
        $result = $this->save();
        
        if($result) {
            $this->_data = null;
        }
        
        return $result;
    }
    
    public function save() {
        $str = "";
        $users = $this->getAll();

        foreach ($users as $name => $userData) {
            $dataStr = "{$userData['hash_method']},{$userData['hash_data']}";
            $str .= "{$name}={$dataStr}\n";
        }

        $path = $this->usersLocatedPath();
        $dir = dirname($path);

        if(!is_dir($dir)) {
            mkdir($dir, 0777, true);
        }

        return file_put_contents($path, $str);
    }
    
    public function get($user) {
        if(!$this->_data) {
            $this->getAll();
        }
        
        return $this->_data[$user];
    }
    
    public function verify($user, $password) {
        $userData = $this->get($user);
        $hashMethod = $userData['hash_method'];
        $hashData = $userData['hash_data'];
        
        if($hashMethod == self::HASHMETHOD_UNKNOWN) {
            return false;
        }
        
        if($hashMethod == self::HASHMETHOD_NONE) {
            return $password == '';
        }

        switch ($hashMethod) {
            case self::HASHMETHOD_MD5:
                $key = substr($hashData, 0, strlen($hashData) - 2);
                return strtolower(md5($key.$password).$key) == $hashData;
                break;
                
            case self::HASHMETHOD_SHA1:
                $key = substr($hashData, 0, strlen($hashData) - 2);
                return strtolower(sha1($key.$password).$key) == $hashData;
                break;
                
            case self::HASHMETHOD_PWDHASH:
                return password_verify($password, $hashData);
                break;
        }

        return false;
    }
}